package vip.sweet.aop.request;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import vip.sweet.aop.annotations.LocalLock;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * @author ASUS
 * @Date 2025/4/27 12:01
 * @Descripition
 */
@Aspect
@Component
@Slf4j
public class LocalLockInterceptor {

    private static final Cache<String, Boolean> CACHE = Caffeine.newBuilder()
            .expireAfterWrite(5, TimeUnit.SECONDS)
            .maximumSize(1000)
            .build();

    @Around("@annotation(vip.sweet.aop.annotations.LocalLock)")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        LocalLock annotation = method.getAnnotation(LocalLock.class);

        String key = getKey(annotation.key(), pjp);
        if (StringUtils.isEmpty(key)) {
            return pjp.proceed();
        }

        log.info("生成 key: {}", key);

        if (CACHE.asMap().putIfAbsent(key, true) != null) {
            log.warn("重复请求被拦截: {}", key);
            throw new RuntimeException("请勿重复提交");
        }

        Object result;
        boolean shouldRemove = false;
        try {
            result = pjp.proceed();
            shouldRemove = true;
        } catch (Exception e) {
            log.error("拦截器异常", e);
            throw e;
        } finally {
            if (shouldRemove) {
                CACHE.invalidate(key);
            }
        }

        return result;
    }

    private String getKey(String keyExpression, ProceedingJoinPoint pjp) {
        if (StringUtils.isBlank(keyExpression)) {
            return "";
        }

        // 替换 {#xxx} 或 #xxx 为 SpEL 拼接格式
        keyExpression = keyExpression
                .replaceAll("\\{#([a-zA-Z0-9\\.]+)\\}", "' + $1")  // 处理 {#xxx}
                .replaceAll("#([a-zA-Z0-9\\.]+)", "$1");           // 处理 #xxx

        // 如果表达式不是纯变量，且不以 ' 开头，说明是拼接格式，需要包裹成字符串
        if (!keyExpression.startsWith("'") && keyExpression.contains(":")) {
            // 将整个表达式拆分成字符串部分 + 变量部分
            String[] parts = keyExpression.split("(?='\\+)|(?=\\+)"); // 按照 '+' 分割
            StringBuilder sb = new StringBuilder();

            for (String part : parts) {
                if (part.trim().startsWith("'")) {
                    sb.append(part);
                } else if (!part.trim().isEmpty()) {
                    sb.append("'").append(part).append("'");
                }
                sb.append("+");
            }

            if (sb.length() > 0) {
                sb.deleteCharAt(sb.length() - 1); // 删除最后一个 +
            }

            keyExpression = sb.toString();
        }

        // 确保表达式开头是 '，否则手动包裹
        if (!keyExpression.trim().startsWith("'")) {
            keyExpression = "'" + keyExpression + "'";
        }

        MethodSignature signature = (MethodSignature) pjp.getSignature();
        String[] paramNames = signature.getParameterNames();
        Object[] args = pjp.getArgs();

        ExpressionParser parser = new SpelExpressionParser();
        EvaluationContext context = new StandardEvaluationContext();

        for (int i = 0; i < args.length; i++) {
            context.setVariable(paramNames[i], args[i]);
        }

        return parser.parseExpression(keyExpression).getValue(context, String.class);
    }
}



